package com.aivin.maproutelib.area;


import com.aivin.maproutelib.bean.WaypointInfo;
import com.aivin.maproutelib.line.ScanLine;
import com.aivin.maproutelib.line.HelperLine;
import com.aivin.maproutelib.tool.MapTool;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


/**
 *  障碍物区域
 */
public class AreaObstacle  {
    /**
     * 航线集合中的最后一个点，与，（扫描线与障碍物区域两个交点） 的最短距离。 <br>
     * 用来排序的。 <br>
     * 因为一个扫描线可能会经过多个障碍物区域。<br>
     */
    double distanceToPoint;

    private List<WaypointInfo> obstacleRoute1 = new ArrayList<>();
    private List<WaypointInfo> obstacleRoute2 = new ArrayList<>();

    /**
     * 障碍物 所有边线
     */
    private List<HelperLine> areaObstacleLines;

    /**
     * 当前扫描线 与 避障区的交点
     */
    List<WaypointInfo> intersectionPointsOfScanLineAndObstacleArea;


    /**
     * @param points 障碍物坐标点
     */
    public AreaObstacle(WaypointInfo... points) {
        List<WaypointInfo> obstaclePoints = new ArrayList<>(Arrays.asList(points));
        intersectionPointsOfScanLineAndObstacleArea = new ArrayList<>();
        areaObstacleLines = new ArrayList<>();

        for (int i = 0; i < obstaclePoints.size(); i++) {
            WaypointInfo start = obstaclePoints.get(i);
            boolean isTheLastPoint = (i == obstaclePoints.size() - 1);
            WaypointInfo endPoint =  isTheLastPoint ?  obstaclePoints.get(0) : obstaclePoints.get(i + 1);

            areaObstacleLines.add( new HelperLine(start, endPoint) );
        }

    }





    /**
     * 用当前扫描线 b=xx ，并且判断是否要已经扫描完了整个地块
     * @param  scanLine ,这个线条是 容器中最后一个点 和将要加入容器中的点组成的线段
     *@param  route 最后的航点坐标 容器 ，满足条件的点加入到这里
     */
    void isScanFinished(ScanLine scanLine , List<WaypointInfo> route) {
        //  scanLine 与 障碍物区域的交点
        intersectionPointsOfScanLineAndObstacleArea.clear();
        for (HelperLine borderLine : areaObstacleLines) {
            WaypointInfo intersectionPoint = MapTool.getIntersectionPoint(borderLine ,scanLine);
            if (intersectionPoint != null) {
                intersectionPointsOfScanLineAndObstacleArea.add(intersectionPoint);
            }
        }

        if (intersectionPointsOfScanLineAndObstacleArea.size() != 2) {
            //当前扫描线没有穿过障碍区 ，中断计算
            return  ;
        }

        obstacleRoute1.clear();
        obstacleRoute2.clear();

        WaypointInfo lastRoutePoint = route.get(route.size() - 1) ;
        WaypointInfo flyPoint = getTheFlyPoint( lastRoutePoint);
        obstacleRoute1.add(flyPoint);
        obstacleRoute2.add(flyPoint);

        WaypointInfo endPoint = intersectionPointsOfScanLineAndObstacleArea.get(0);
        List<WaypointInfo> obstacleRoute = createObstacleRoute(flyPoint, endPoint);

        if (obstacleRoute != null) {
            route.addAll(obstacleRoute);
        }

    }


    /**
     * @param cPoint 扫描线与障碍区的交点 1
     * @param endPoint 扫描线与障碍区的交点 2
     */
    private List<WaypointInfo> createObstacleRoute(WaypointInfo cPoint, WaypointInfo endPoint) {
        HelperLine cLine = null;
        HelperLine endLine = null;
        for (HelperLine line : areaObstacleLines) {
            if (MapTool.isOnLine(cPoint ,line) ) {
                // cPoint 交点所在的障碍物边框线
                cLine = line;
                break;
            }
        }
        for (HelperLine line : areaObstacleLines) {
            if (MapTool.isOnLine(endPoint ,line)) {
                // endPoint 交点所在的障碍物边框线
                endLine = line;
                break;
            }
        }

        if (cLine == null || endLine == null) {
            throw new RuntimeException("发生未知异常...");
        }

        WaypointInfo point1 = cLine.endPoint;
        WaypointInfo point2 = cLine.startPoint;

        obstacleRoute1.add(new WaypointInfo(point1.getLatitude(),point1.getLongitude()));
        obstacleRoute2.add(new WaypointInfo(point2.getLatitude(),point2.getLongitude()));

        WaypointInfo epoint1 = endLine.endPoint;
        WaypointInfo epoint2 = endLine.startPoint;

        createObstacleRoute(obstacleRoute1, point1, point2, epoint1, epoint2);
        createObstacleRoute(obstacleRoute2, point2, point1, epoint1, epoint2);

        setLastPoint(obstacleRoute1);
        setLastPoint(obstacleRoute2);
        return getTheShorterRouter(obstacleRoute1, obstacleRoute2);
    }


    /**
     * 从两条路径中找短的那个
     */
    private List<WaypointInfo> getTheShorterRouter(List<WaypointInfo> obstacleRoute1,
                                                   List<WaypointInfo> obstacleRoute2) {
        double sumDistance1 = MapTool.sumDistance(obstacleRoute1);
        double sumDistance2 = MapTool.sumDistance(obstacleRoute2);
        return sumDistance1 > sumDistance2 ? obstacleRoute2 : obstacleRoute1;
    }




    private void createObstacleRoute(List<WaypointInfo> obstacleRoute
            , WaypointInfo cPoint, WaypointInfo point, WaypointInfo end1, WaypointInfo end2) {

        if (cPoint.equals(end1)) {
            return;
        } else if (cPoint.equals(end2)) {
            return;
        }

        List<HelperLine> passMe = cPoint.getpassedMeLines();
        List<WaypointInfo> points = new ArrayList<>();
        for (HelperLine line : passMe) {
            WaypointInfo startPoint = line.startPoint;
            WaypointInfo endPoint = line.endPoint;
            if (!points.contains(startPoint)) {
                points.add(startPoint);
            }
            if (!points.contains(endPoint)) {
                points.add(endPoint);
            }
        }
        points.remove(cPoint);
        points.remove(point);

        if (points.size() == 1) {
            WaypointInfo nextPoint = points.get(0);
            obstacleRoute.add(new WaypointInfo(nextPoint.getLatitude(),nextPoint.getLongitude()/*,nextPoint.isObstacle*/));
            createObstacleRoute(obstacleRoute, nextPoint, cPoint, end1, end2);
        }
    }


    /**
     * 第二个障碍物点添加上去  即：最后一个点添加上去  本次扫描结束
     */
    private void setLastPoint(List<WaypointInfo> route) {
        WaypointInfo po = intersectionPointsOfScanLineAndObstacleArea.get(0);
        route.add(po);
    }



    /**
     * @param waypointInfo 航点中最后一个
     * @return 当前扫描线与 障碍物区 的交点作为一个航点 返回
     */
    private WaypointInfo getTheFlyPoint(WaypointInfo waypointInfo) {
        WaypointInfo point0 = intersectionPointsOfScanLineAndObstacleArea.get(0);
        WaypointInfo point1 = intersectionPointsOfScanLineAndObstacleArea.get(1);

        double distance0 = MapTool.calculationTwoPointDistance(waypointInfo, point0);
        double distance1 = MapTool.calculationTwoPointDistance(waypointInfo, point1);

        if (distance0 > distance1) {
            intersectionPointsOfScanLineAndObstacleArea.remove(point1);
            return point1;
        } else {
            intersectionPointsOfScanLineAndObstacleArea.remove(point0);
            return point0;
        }
    }
}
